#include "CD3D9RenderSystem.h"

#include "Render\CCamera.h"
#include "Render\CMesh.h"
#include "Core\CWindow.h"
#include "Render\CMaterial.h"
#include "Render\CTexture.h"
#include "Core\CChrono.h"

#include "D3D9\CD3D9Mesh.h"
#include "D3D9\CD3D9Sprite.h"

namespace Nervus {
	namespace Render {
		//----------------------------------------------------------------------------------
		D3D9RenderSystem::D3D9RenderSystem(Core::Window* wnd)
			: mMainDX(NULL), mFieldOfView(60), mVideoCard(NULL)
		{
			mRenderWnd = wnd;
		}
		//----------------------------------------------------------------------------------
		D3D9RenderSystem::~D3D9RenderSystem(){

			if (mVideoCard != NULL)
				mVideoCard->Release();
			if (mMainDX != NULL)
				mMainDX->Release();
		}
		//----------------------------------------------------------------------------------
		bool D3D9RenderSystem::launchRenderSystem(){
			if (mVideoCard != NULL){
				NERVUS_ERROR("CD3D9RenderSystem: System already launched");
				return false;
			}

			if ((mMainDX = Direct3DCreate9(D3D_SDK_VERSION)) == NULL){
				NERVUS_ERROR("CD3D9RenderSystem: Failed to create D3D");
				return false;
			}


			setDXSettings();


			if (FAILED(mMainDX->CreateDevice(D3DADAPTER_DEFAULT,
				D3DDEVTYPE_HAL,
				mRenderWnd->mWndDesc,
				D3DCREATE_SOFTWARE_VERTEXPROCESSING,
				&mDXParams, &mVideoCard))){


				NERVUS_ERROR("CD3D9RenderSystem: Failed to create DX device");
				return false;
			}


			setRenderStates();
			setProjectionMatrix();
			

			//Texture* mDefaultTextureFile = new Texture(DEFAULT_TEXTURE);
			//loadTexture(mDefaultTextureFile, mDefaultTexture);
			mDefaultTexture = NULL;


			return true;
		}
		//----------------------------------------------------------------------------------
		bool D3D9RenderSystem::setDXSettings(){
			ZeroMemory(&mDXParams, sizeof(D3DPRESENT_PARAMETERS));
			mDXParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
			mDXParams.EnableAutoDepthStencil = TRUE;
			mDXParams.AutoDepthStencilFormat = D3DFMT_D16;

			if (mFullscreen) {
				D3DDISPLAYMODE dispMode = { 0 };
				if (FAILED(mMainDX->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dispMode))){
					NERVUS_ERROR("CD3D9RenderSystem: Failed to get display params");
					return false;
				}
				mBufferWidth = dispMode.Width;
				mBufferHeight = dispMode.Height;

				mDXParams.Windowed = false;
				mDXParams.BackBufferWidth = dispMode.Width;
				mDXParams.BackBufferHeight = dispMode.Height;
				mDXParams.FullScreen_RefreshRateInHz = dispMode.RefreshRate;
				mDXParams.BackBufferFormat = dispMode.Format;
			} else {
				RECT clientRect = { 0 };
				if (!GetClientRect(mRenderWnd->mWndDesc, &clientRect)){
					NERVUS_ERROR("CD3D9RenderSystem: Failed to get client area");
					return false;
				}
				mBufferWidth = clientRect.right;
				mBufferHeight = clientRect.bottom;

				mDXParams.Windowed = true;
				mDXParams.BackBufferWidth = clientRect.right;
				mDXParams.BackBufferHeight = clientRect.bottom;
				mDXParams.BackBufferFormat = D3DFMT_UNKNOWN;
			}
			return true;
		}
		//----------------------------------------------------------------------------------
		void D3D9RenderSystem::setRenderStates(){
			//mVideoCard->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);

			mVideoCard->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);


			mVideoCard->SetRenderState(D3DRS_LIGHTING, FALSE);
			mVideoCard->SetRenderState(D3DRS_ZENABLE, TRUE);
			mVideoCard->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
			mVideoCard->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
			mVideoCard->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
			mVideoCard->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
			mVideoCard->SetRenderState(D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD);
			mVideoCard->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
			mVideoCard->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
			mVideoCard->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
		}
		//----------------------------------------------------------------------------------
		void D3D9RenderSystem::setProjectionMatrix(){
			RECT rct;
			GetClientRect(mRenderWnd->mWndDesc, &rct);
			f32 aspect = (rct.right - rct.left) / (f32)(rct.bottom - rct.top);

			//float aspect = mBufferWidth / (float) mBufferHeight;

			D3DXMATRIX matProj;
			D3DXMatrixPerspectiveFovLH(&matProj, D3DXToRadian(mFieldOfView), aspect, 1.0f, 100.0f);
			mVideoCard->SetTransform(D3DTS_PROJECTION, &matProj);
		}
		//----------------------------------------------------------------------------------
		bool D3D9RenderSystem::toggleFullscreen(){
			if (mVideoCard == NULL){
				return false;
			}
			mFullscreen = !mFullscreen;

			setDXSettings();
			
			if (mFullscreen) {
				GetWindowRect(mRenderWnd->mWndDesc, &mRenderWnd->mWndRect);
			}

			if (mVideoCard->Reset(&mDXParams) != D3D_OK) {
				mFullscreen = !mFullscreen;
				NERVUS_ERROR("CD3D9RenderSystem: Failed to reset video device");
				return false;
			}

			if (mFullscreen == false) {
				SetWindowPos(mRenderWnd->mWndDesc, HWND_NOTOPMOST, mRenderWnd->mWndRect.left, 
					mRenderWnd->mWndRect.top, mRenderWnd->mWndRect.right - mRenderWnd->mWndRect.left,
					mRenderWnd->mWndRect.bottom - mRenderWnd->mWndRect.top, SWP_SHOWWINDOW);
			}


			setRenderStates();
			setProjectionMatrix();
			
			return true;
		}
		//----------------------------------------------------------------------------------



		//----------------------------------------------------------------------------------
		//----------------------------------------------------------------------------------
		void D3D9RenderSystem::beginRender(f32 interpolation){
			mCamera->updateCam();
			if (interpolation == -1.0f) {
				mVideoCard->SetTransform(D3DTS_VIEW, mCamera->getViewMatrix());
			} else {
				mVideoCard->SetTransform(D3DTS_VIEW, mCamera->getRenderViewMatrix(interpolation));
			}



			mVideoCard->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(0, 77, 77, 77), 1.0f, 0);
			mVideoCard->BeginScene();

		}
		//----------------------------------------------------------------------------------
		void D3D9RenderSystem::endRender(){
			mVideoCard->EndScene();
			mVideoCard->Present(NULL, NULL, NULL, NULL);
		}
		//----------------------------------------------------------------------------------
		Mesh* D3D9RenderSystem::createMesh(vector<MeshVertex> vrts, DWORD* indxs){
			Mesh* msh = new D3D9Mesh(this, vrts);
			//msh->loadMesh();


			return msh;
		}
		//----------------------------------------------------------------------------------
		Sprite* D3D9RenderSystem::createSprite(D3DXVECTOR2 pos, Texture* tex){
			Sprite* spr = new D3D9Sprite(this, pos, tex);
			//spr->loadSprite();


			return spr;
		}
		//----------------------------------------------------------------------------------
		//----------------------------------------------------------------------------------
		/*//////*/void D3D9RenderSystem::shaderOne(){


		}
		//----------------------------------------------------------------------------------
		/*//////*/void D3D9RenderSystem::shaderTwo(){

		}
		//----------------------------------------------------------------------------------
		void D3D9RenderSystem::enable3D(){

		}
		//----------------------------------------------------------------------------------
		void D3D9RenderSystem::enable2D(){

		}
	}
}